package lol.clann.minecraft.springboot.api.utils;

import com.ruoshui.utils.java.exception.ExceptionUtils;
import com.ruoshui.utils.java.reflect.ReflectUtils;
import lol.clann.minecraft.springboot.api.annotation.config.Configure;
import lol.clann.minecraft.springboot.api.annotation.config.PostConfig;
import lol.clann.minecraft.springboot.api.annotation.config.PreConfig;
import lol.clann.minecraft.springboot.api.annotation.config.TranslateColoreCode;
import lol.clann.minecraft.springboot.api.context.PluginContext;
import lol.clann.minecraft.springboot.api.model.core.Plugin;
import lol.clann.minecraft.springboot.api.model.message.MessageBuilder;
import relocate.com.google.common.reflect.TypeToken;
import relocate.ninja.leaping.configurate.commented.CommentedConfigurationNode;
import relocate.ninja.leaping.configurate.objectmapping.ObjectMappingException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.bukkit.ChatColor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * 配置注入逻辑
 *
 * @author pyz
 * @date 2019/5/1 10:25 PM
 */
@Slf4j
public class ConfigUtils {

    public static void resolve(Object bean) throws IllegalAccessException {
//      只对非spring的bena进行配置注入
        if (bean.getClass().getName().startsWith("org.springframework.")) {
            return;
        }
        Class clazz = ClassUtils.getUserClass(bean);
        Plugin plugin = PluginContext.getPluginByClass(clazz);
        if (plugin == null) {
            return;
        }
        resolve(plugin, bean);
    }

    public static void resolve(Plugin plugin, Object bean) throws IllegalAccessException {
        CallbackUtils.invok(bean, PreConfig.class);
        long count = injectConfigure(plugin.getConfigurationNode(), bean);
        if (count > 0) {
            LOGGER.info("apply config to {}", bean.getClass().getName());
        }
        CallbackUtils.invok(bean, PostConfig.class);
    }

    /**
     * 这个会警告,所以集中起来
     *
     * @param node
     * @param path
     * @return
     */
    @SuppressWarnings("")
    private static CommentedConfigurationNode getNode(CommentedConfigurationNode node, String path) {
        return node.getNode(path.split("\\."));
    }

    public static long saveConfig(CommentedConfigurationNode root, Object o) throws IllegalAccessException {
        int total = 0;

        for (Field field : ReflectUtils.getAllFields(o.getClass())) {
            if (field.isSynthetic() || Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
                continue;
            }
            Configure path = AnnotationUtils.findAnnotation(field, Configure.class);
            if (path == null) {
                continue;
            }
            total++;
            if (!Modifier.isPublic(field.getModifiers())) {
                field.setAccessible(true);
            }
            CommentedConfigurationNode node = getNode(root, path.value());
            setDefault(o, field, getComment(path), node);
        }
        return total;
    }

    private static long injectConfigure(CommentedConfigurationNode root, Object o) throws IllegalAccessException {
        int total = 0;
        for (Field field : ReflectUtils.getAllFields(o.getClass())) {
            if (field.isSynthetic() || Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
                continue;
            }
            Configure path = AnnotationUtils.findAnnotation(field, Configure.class);
            if (path == null) {
                continue;
            }
            total++;
            if (!Modifier.isPublic(field.getModifiers())) {
                field.setAccessible(true);
            }
            CommentedConfigurationNode node = getNode(root, path.value());
            if (node == null) {
                continue;
            }
            if (field.get(o) == null) {//没有默认值
                //集合默认设置为空
                if (List.class.isAssignableFrom(field.getType())) {
                    field.set(o, new ArrayList<>());
                } else if (Set.class.isAssignableFrom(field.getType())) {
                    field.set(o, new HashSet<>());
                } else if (Map.class.isAssignableFrom(field.getType())) {
                    field.set(o, new HashMap<>());
                }
            }
            if (node.isVirtual()) {
                setDefault(o, field, getComment(path), node);
                continue;
            }
            Object value = getValue(node, field);
            if (value != null) {
                if (AnnotationUtils.findAnnotation(field, TranslateColoreCode.class) != null) {
                    value = translate(value);
                }
                field.set(o, value);
            }
        }
        return total;
    }

    private static void setDefault(Object o, Field field, String comment, CommentedConfigurationNode node) throws IllegalAccessException {
        Object value = field.get(o);
        if (value == null) {
            return;
        } else {
            TypeToken typeToken = TypeToken.of(field.getGenericType());
            if (AnnotationUtils.findAnnotation(field, TranslateColoreCode.class) != null) {
                value = translate(value);
            }
            try {
                node.setValue(typeToken, value);
            } catch (ObjectMappingException e) {
                ExceptionUtils.throwException(e);
            }
        }
        if (StringUtils.isNotBlank(comment)) {
            node.setComment(comment);
        }
    }

    private static Object getValue(CommentedConfigurationNode node, Field field) {
        TypeToken<?> token = TypeToken.of(field.getGenericType());
        try {
            return node.getValue(token);
        } catch (ObjectMappingException e) {
            ExceptionUtils.throwException(e);
            return null;
        }
    }

    private static String getComment(Configure config) {
        return config.comment();
    }

    /**
     * 转换颜色字符
     *
     * @param v
     * @return
     * @throws IllegalAccessException
     */
    private static Object translate(Object v) throws IllegalAccessException {
        if (v == null) {
            return v;
        }
        if (v instanceof String) {
            return ChatColor.translateAlternateColorCodes('&', (String) v);
        }
        if (ClassUtils.isPrimitiveOrWrapper(v.getClass())) {
            return v;
        }
//        这个类需要特殊处理
        if (v.getClass() == MessageBuilder.class) {
            return MessageBuilder.from(ChatColor.translateAlternateColorCodes('&', ((MessageBuilder) v).getRawText()));
        }
        if (v instanceof List) {
            List<Object> olds = (List<Object>) v;
            List<Object> list = new ArrayList<>(olds.size());
            for (Object old : olds) {
                list.add(translate(old));
            }
            return olds;
        } else if (v instanceof Set) {
            Set<Object> olds = (Set<Object>) v;
            Set<Object> list = new HashSet<>(olds.size());
            for (Object old : olds) {
                list.add(translate(old));
            }
            return olds;
        } else if (v instanceof Map) {
            Map<?, ?> oldMap = (Map) v;
            Map newMap = new HashMap();
            for (Map.Entry en : oldMap.entrySet()) {
                newMap.put(en.getKey(), translate(en.getValue()));
            }
            return newMap;
        } else {
//            继续解析
            for (Field field : ReflectUtils.getAllFields(v.getClass())) {
                if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
                    continue;
                }
                if (AnnotationUtils.findAnnotation(field, TranslateColoreCode.class) == null) {
                    continue;
                }
                field.setAccessible(true);
                field.set(v, translate(field.get(v)));
            }
            return v;
        }
    }
}
